package hexgame;


public class HexGame implements Runnable {
	
	GameState state;
	Player observer;
	Thread thread;
	HexGameRequest request;
	boolean didSendRequest;
	boolean gameEnded = false;
	boolean localPlayerWon;
	
	/**
	 * Creates a new HexGame from a request object.
	 * HexGame.startGame should be used to start the game in a new thread
	 * @param request - The HexGameRequest storing the required information
	 */
	public HexGame(HexGameRequest request) {
		this.request = request;
		didSendRequest = request.didSendRequest;
		thread = new Thread(this);
	}
	
	/**
	 * Runs the game on a new thread
	 */
	public void startGame() {
		thread.start();
	}
	
	/**
	 * Runs the game logic will return when the game has ended (either from a win or from an error)
	 */
	@Override
	public void run() {
		state = new GameState(request);
		state.localPlayer = request.localPlayerFactory.createPlayer(state);
		
		if(request.observerFactory != null) {
		
			this.observer = request.observerFactory.createPlayer(state);
		}
		
		// do game loop
		while(GameLogic.playerWon(state) == HexPlayer.NONE) {
			try{
				if(state.localPlayerTurn)
				{
					executeMove(state.localPlayer, state.remotePlayer, observer);
				}
				else
				{
					executeMove(state.remotePlayer, state.localPlayer, observer);
				}
			}
			catch(HexGameMoveException e) {
				Log("Error occured. Ending Game");
				return;
			}
		}
		gameEnded = true;
		localPlayerWon = GameLogic.playerWon(state) == HexPlayer.RED_LOCAL;
	
		state.localPlayer.gameOver(localPlayerWon);
		state.remotePlayer.gameOver(!localPlayerWon);
	}
	
	/**
	 * Gets a move from the first player and sends it to the second player after checking it's validity and applying the move
	 * to the GameState.
	 * @throws HexGameMoveException on an error either getting the move or if the move isn't valid
	 */
	private void executeMove(Player first, Player second, Player observer) throws HexGameMoveException {
 		HexMove move = first.getMove();
		
		if(move == null) {
			first.sendErrorMessage("Error getting move from you. Game disconecting");
			second.sendErrorMessage("Error getting move from other player. Game disconecting");
			Log("Error getting move from player " + first.getClass().toString() + ". Game disconecting");
			throw new HexGameMoveException("Error getting move from player " + first.getClass().toString() + ". Game disconecting");
		}
		
		if(GameLogic.isValidMove(state, move))
		{
			GameLogic.makeMove(state, move);
			second.showMove(move);
			if(observer != null) {
				observer.showMove(move);
			}
			Log("Valid move " + move.getPlayer() + " " + move.x + " " + move.y);
		}
		else
		{
			first.sendErrorMessage("Invalid move submitted from you " + move.getPlayer() + " " + move.x + " " + move.y + " . Game disconecting");
			second.sendErrorMessage("Invalid move submitted from other player " + move.getPlayer() + " " + move.x + " " + move.y + ". Game disconecting");
			Log("Invalid move submitted from player " + first.getClass().toString() + ". Game disconecting");
			throw new HexGameMoveException("Invalid move submitted from player " + first.getClass().toString() + ". Game disconecting");
		}
	}
	
	/**
	 * Used to format output messages and display which game they came from
	 * @param msg
	 */
	public void Log(String msg) {
		System.out.println("(" + (didSendRequest ? "Client" : "Server") + ") " + msg);
	}
	
	public boolean hasEnded() {
		return gameEnded;
	}
	
	public boolean hasLocalPlayerWon() {
		return localPlayerWon;
	}
	
	public HexGameRequest getRequest() {
		return request;
	}
	
 }
